﻿Imports System
Imports System.IO
Imports System.Net
Imports System.Text
Imports System.Web.Script.Serialization

Public Class cURLPush
    
    Private _TableName As String = ""

    Public Property TableName As String
        Get
            Return _TableName
        End Get
        Set(ByVal value As String)
            _TableName = value
        End Set
    End Property

    Public Sub New(ByVal TableName As String)
        _TableName = TableName
    End Sub

    ''' <summary>
    ''' Curls the latest data load to the VOEIS hub.
    ''' </summary>
    ''' <remarks>This is the workhorse method for pushing the data to the hub - it builds the request and consumes the response.</remarks>
    Public Sub Curl2Hub()
        Dim DC As New DataCollector()
        Dim propArrayList As New ArrayList

        'Populate our recordset
        DC.SetPushRecords(TableName)

        'Populate our stream metadata
        DC.SetStreamMetadata(TableName)

        'Set up some of our basic variables
        Dim HubURL As String = My.Settings.VOEISHubURL.Replace("<projectID>", DC.ProjectID) & "upload_logger_data.json?api_key=" & My.Settings.APIKey

        'Set up the header boundary
        Dim boundary As String = "----------------------------" & DateTime.Now.Ticks.ToString("x")

        'Set up the boundary sentences as byte arrays
        Dim boundarybytes As Byte() = System.Text.Encoding.ASCII.GetBytes("--" & boundary & Environment.NewLine)
        Dim boundaryBytes2 As Byte() = Text.Encoding.ASCII.GetBytes(Environment.NewLine & "--" & boundary & Environment.NewLine)

        'Notify via the event log regarding what I am doing
        System.Diagnostics.EventLog.WriteEntry("VOEIS Hub Push Service", String.Format("Uploading {0} to {1}", DC.FileName, HubURL))

        'Set security protocols
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3

        'Create the web request and set some basic parameters
        Dim request As HttpWebRequest = DirectCast(WebRequest.Create(New Uri(HubURL, UriKind.Absolute)), HttpWebRequest)

        'Set the timeout for the response (default is 30 mins)
        request.Timeout = My.Settings.HubTimeout

        request.Method = "POST"
        request.AllowAutoRedirect = True
        request.KeepAlive = True
        request.Credentials = CredentialCache.DefaultCredentials
        request.ContentType = "multipart/form-data; boundary=" & boundary

        'Set up the request stream
        Dim requestStream As Stream = request.GetRequestStream()

        'Write the first boundary
        requestStream.Write(boundarybytes, 0, boundarybytes.Length)

        'Begin header setup for the filestream upload
        'Setup the template
        Dim FileStreamHeaderTemplate As String = "Content-Disposition: form-data; name=""{0}""; filename=""{1}""" & Environment.NewLine & "Content-Type: {2}" & Environment.NewLine & Environment.NewLine
        'Create the header string
        Dim FileStreamHeader As String = String.Format(FileStreamHeaderTemplate, "datafile", DC.FileName, "application/octet-stream")
        'Convert the header to a byte array
        Dim FileStreamHeaderBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(FileStreamHeader)
        'Write the header bytes to the request stream
        requestStream.Write(FileStreamHeaderBytes, 0, FileStreamHeaderBytes.Length)

        'Declare our buffer byte array
        Dim buffer As Byte() = New Byte(4095) {}
        'Setup a counter to track the bytes read
        Dim bytesRead As Integer = 0
        'Create a memory stream from our pushrecords variable
        Dim ms As New MemoryStream(ASCIIEncoding.Default.GetBytes(DC.PushRecords.ToString()))
        'Move to the beginning of the memory stream
        ms.Seek(0, SeekOrigin.Begin)
        'Begin outputting the memory stream to the request stream
        While (InlineAssignHelper(bytesRead, ms.Read(buffer, 0, buffer.Length))) <> 0
            requestStream.Write(buffer, 0, bytesRead)
        End While
        'Close the memory stream
        ms.Close()

        'Write the boundary header to the request stream
        'We are having issues when there is only one record being sent.  I am going to add in a Environment.NewLine to see the effect.
        requestStream.Write(boundaryBytes2, 0, boundaryBytes2.Length)

        'Begin header setup for the form field upload
        'Setup the form data template
        Dim FormDataTemplate As String = "Content-Disposition: form-data; name=""{0}""" & Environment.NewLine & Environment.NewLine & "{1}"
        'Setup the form header string
        Dim FormItem As String = String.Format(FormDataTemplate, "data_template_id", DC.DataTemplateID.ToString)
        'Convert the header string to a byte array
        Dim FormItemBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(FormItem)
        'Write the header bytes to the request stream
        requestStream.Write(FormItemBytes, 0, FormItemBytes.Length)

        'Write the trailing boundary
        'Setup the trailing boundary byte array
        Dim trailer As Byte() = System.Text.Encoding.ASCII.GetBytes(Environment.NewLine & "--" & boundary & "--" & Environment.NewLine)
        'Write the trailing boundary byte array to the request stream
        requestStream.Write(trailer, 0, trailer.Length)
        'Close the request stream
        requestStream.Close()

        'Begin receiving our response
        Dim response As WebResponse = Nothing
        Try
            EventLog.WriteEntry("VOEIS Hub Push Service", "Waiting for response for stream after uploading " & DC.FileName)
            'Instantiate the response
            response = request.GetResponse()
            'Setup the response stream
            Dim responseStream As Stream = response.GetResponseStream()

            'Begin a stream reader for the response stream
            Dim sr As New StreamReader(responseStream)
            Dim responseString As String = sr.ReadToEnd()

            'Write the response stream to the event log
            System.Diagnostics.EventLog.WriteEntry("VOEIS Hub Push Service", String.Format("File {0} uploaded, server response is: {1}", DC.FileName, responseString))

            'Log the response data in the database
            Dim dw As New DataWriter(DC.SyncID)
            dw.UpdateSyncMetadata(TableName, DC.LastRecordDateTime, responseString)

        Catch ex As Exception
            'Catch our errors and write them to the event log and send an email
            System.Diagnostics.EventLog.WriteEntry("VOEIS Hub Push Service", "Error in cURLPush class - Curl2Hub." & Environment.NewLine & "Error uploading file " & DC.FileName & ": " & Environment.NewLine & ex.Message.ToString, EventLogEntryType.Error)

            'TODO: Email the exception and setup the current state
            Dim mailer As New SendMail()
            mailer.SendServiceMail("VOEIS Hub Push Service", "Error in cURLPush class - Curl2Hub." & Environment.NewLine & "Error uploading file " & DC.FileName & ": " & Environment.NewLine & ex.Message.ToString)

        Finally
            'Make sure that we close the response if it is still alive
            If response IsNot Nothing Then
                response.Close()
                response = Nothing
            End If

            'Make sure that we close our request
            request = Nothing

        End Try
    End Sub

    ''' <summary>
    ''' Parses data byte wise.
    ''' </summary>
    ''' <typeparam name="T"></typeparam>
    ''' <param name="target"></param>
    ''' <param name="value"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
        target = value
        Return value
    End Function

End Class
